<template>
    <div v-show="showCtrl" class="vmock">
        <el-dropdown :type="btnType" split-button trigger="click" @click="handleChangeMockRecord">
            {{ btnText }}
            <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item @click="handleChangeMockRecord">{{
                            isMockRecord ? '关闭' : '开启'
                        }}mock记录
                    </el-dropdown-item>
                    <el-dropdown-item @click="handleChangeMockMode">{{
                            isMockMode ? '关闭' : '开启'
                        }}mock模式
                    </el-dropdown-item>
                    <el-dropdown-item @click="handleClearJson">清空本地mock数据</el-dropdown-item>
                    <el-dropdown-item @click="showD13n = true">脱敏规则设置</el-dropdown-item>
                    <el-dropdown-item @click="handleDownloadJson">下载json文件</el-dropdown-item>
                    <el-upload
                            ref="uploadRef"
                            :before-upload="handleBeforeUpload"
                            :show-file-list="false"
                            class="upload"
                    >
                        <el-dropdown-item>
                            导入json文件
                        </el-dropdown-item>
                    </el-upload>
                    <el-dropdown-item @click="showCtrl = false">
                        <el-tooltip
                                class="box-item"
                                content="切换快捷键【 Shift + M 】"
                                placement="left"
                        >
                            关闭当前按钮
                        </el-tooltip>
                    </el-dropdown-item>
                </el-dropdown-menu>
            </template>
        </el-dropdown>
    </div>
    <el-dialog v-model="showD13n" title="脱敏规则设置" width="90%">
        <el-table :data="d13nData" max-height="550" style="width: 100%;" empty-text="暂无数据">
            <el-table-column label="选择接口" width="300">
                <template #default="scope">
                    <el-input v-model="d13nData[scope.$index].selectInterface" placeholder="空或*代表全部接口，也可使用部分地址路径进行匹配"></el-input>
                </template>
            </el-table-column>
            <el-table-column label="排除接口" width="300">
                <template #default="scope">
                    <el-input v-model="d13nData[scope.$index].excludeInterface" placeholder="*代表全部接口，也可使用部分地址路径进行匹配"></el-input>
                </template>
            </el-table-column>
            <el-table-column label="数据字段" width="300">
                <template #default="scope">
                    <el-input v-model="d13nData[scope.$index].field" placeholder="可使用','连接多个字段统一替换"></el-input>
                </template>
            </el-table-column>
            <el-table-column label="脱敏规则">
                <template #default="scope">
                    <div class="rule">
                        <el-select v-model="d13nData[scope.$index].type" style="width: 650px;">
                            <el-option label="前后保留，中间转换为'*'" value="1"></el-option>
                            <el-option label="全部转换为'*'" value="2"></el-option>
                            <el-option label="转换为随机数字" value="3"></el-option>
                            <el-option label="指定替换字符串" value="4"></el-option>
                            <el-option label="指定正则替换" value="5"></el-option>
                        </el-select>
                        <el-input v-show="scope.row.type !== '2'" v-model="d13nData[scope.$index].rangeMin" :placeholder="minPh(scope)"></el-input>
                        <el-input v-show="scope.row.type !== '2'" v-model="d13nData[scope.$index].rangeMax" :placeholder="maxPh(scope)"></el-input>
                    </div>
                </template>
            </el-table-column>
            <el-table-column label="操作" width="100">
                <template #default="scope">
                    <el-button type="danger" link @click="removeRule(scope)">移除规则</el-button>
                </template>
            </el-table-column>
        </el-table>
        <el-button class="mt-4" style="width: 100%" @click="addRule">
            添加新规则
        </el-button>
    </el-dialog>
</template>

<script lang="ts" setup>
import type {UploadFile} from 'element-plus'
import type {Config} from "../index";

import {computed, nextTick, ref, watch, inject, onMounted, unref} from 'vue'
import {proxy, unProxy} from 'ajax-hook'
import {StorageDB} from '../units/db'
import { nanoid } from "nanoid";
import {ruleFun} from "../units/replace";
import {isArray, isObject} from "../units/isType";

const config = inject('config') as Config
const btnDbKey = config.btnDbKey
const db = new StorageDB(config.dbKey, config.mode, config.indexedDBName)

const showCtrl = ref(!config.hideCtrl)
document.addEventListener("keydown", (e) => {
    if (e.code === 'KeyM' && e.shiftKey) {
        e.preventDefault()
        showCtrl.value = !showCtrl.value
    }
})

const isMockRecord = ref(false) // 是否开启记录
const isMockMode = ref(false) // 是否mock模式
const btnText = computed(() => {
    if (unref(isMockRecord)) {
        return "mock记录已开启"
    }
    if (unref(isMockMode)) {
        return "mock模式已开启"
    }
    return "mock 未运行"
})
const btnType = computed(() => {
    if (unref(isMockRecord)) {
        return "success";
    }
    if (unref(isMockMode)) {
        return "warning"
    }
    return "primary"
})
watch([isMockRecord, isMockMode], ([newRecord, newMockMode]) => {
    unProxy()
    const btnDataJson = localStorage.getItem(btnDbKey)
    let btnData = btnDataJson ? JSON.parse(btnDataJson) : []
    const storageRecord = btnData?.includes('openMockRecord')
    const storageMode = btnData?.includes('openMockMode')
    if (newRecord && !storageRecord) {
        btnData.push('openMockRecord')
    }
    if (!newRecord && storageRecord) {
        btnData = btnData.filter(s => s !== 'openMockRecord')
    }
    if (newMockMode && !storageMode) {
        btnData.push('openMockMode')
    }
    if (!newMockMode && storageMode) {
        btnData = btnData.filter(s => s !== 'openMockMode')
    }
    localStorage.setItem(btnDbKey, JSON.stringify(btnData))
})

const handleChangeMockRecord = () => {
    isMockMode.value = false
    isMockRecord.value = !isMockRecord.value

    nextTick(() => {
        if (isMockRecord.value) {
            proxy({
                onResponse(response, handler) {
                    db.setDB({
                        url: response.config.url,
                        body: response.config.body,
                        method: response.config.method,
                        result: response.response,
                        headers: response.headers
                    })
                    handler.next(response)
                }
            })
        }
    })
}
const handleChangeMockMode = () => {
    isMockRecord.value = false
    isMockMode.value = !isMockMode.value

    nextTick(() => {
        if (isMockMode.value) {
            proxy({
                async onRequest(config, handler) {
                    const datas = await db.getDB()
                    const data = datas.find(d =>
                            d.body === config.body &&
                            d.url === config.url &&
                            d.method === config.method
                    )
                    if (data) {
                        handler.resolve({
                            config,
                            status: 200,
                            headers: data.headers,
                            response: data.result
                        })
                    } else {
                        handler.next(config)
                    }
                }
            })
        }
    })
}
const handleClearJson = () => {
    db.clear()
}
const handleDownloadJson = () => {
    const localRules = window.localStorage.getItem(rulesKey);
    const rules = localRules ? JSON.parse(localRules) : [];
    db.downloadDBJson((list) => {
        if (rules.length === 0) {
            return list;
        }

        return list.map(item => {
            // 筛选适用此请求的规则
            const currentRules: D13NDATA[] = rules.filter(rule => {
                // 校验必填项 字段，替换规则
                if (
                        !rule.field || rule.field.length === 0 ||
                        (rule.type !== "2" && (!rule.rangeMin || !rule.rangeMax))
                ) {
                    return false;
                }
                // 选择地址判断
                if (rule.selectInterface.startsWith('/') && rule.selectInterface.endsWith('/')) {
                    // 正则
                    const reg = new RegExp(rule.selectInterface);
                    if (!reg.test(item.url)) {
                        return false;
                    }
                } else if (rule.selectInterface.length > 0 && rule.selectInterface !== '*' && item.url.indexOf(rule.selectInterface) === -1) {
                    // 字符串
                    return false;
                }
                // 排除地址判断
                if (rule.excludeInterface.startsWith('/') && rule.excludeInterface.endsWith('/')) {
                    // 正则
                    const reg = new RegExp(rule.excludeInterface);
                    if (reg.test(item.url)) {
                        return false;
                    }
                } else if (rule.excludeInterface.length > 0 && item.url.indexOf(rule.excludeInterface) !== -1) {
                    // 字符串
                    return false;
                }
                return true;
            })

            // 通过规则修改item为脱敏数据,没有规则则返回原数据
            if(currentRules.length === 0) {
                return item
            }
            try {
                let result: any
                if (typeof item.result === 'string') {
                    result = JSON.parse(item.result);
                } else if (item.result instanceof Object) {
                    result = item.result;
                }
                for(let i = 0;i < currentRules.length; i++) {
                    const rule = currentRules[i]; // 脱敏规则
                    const fields = rule.field.split(','); // 脱敏字段
                    if (isArray(result)) {
                        result = result.map(r => {
                            for (const field of fields) {
                                r[field] = ruleFun[rule.type](r[field], rule.rangeMin, rule.rangeMax);
                            }
                            return r;
                        })
                    } else if (isObject(result)) {
                        for (const field of fields) {
                            result[field] = ruleFun[rule.type](result[field], rule.rangeMin, rule.rangeMax);
                        }
                    }
                }
                console.log(result);
                item.result = result;
                return item;
            } catch(e) {
                console.log(e)
                return item;
            }
        })
    })
}

const handleBeforeUpload = (uploadFile: UploadFile) => {
    const reader = new FileReader()
    reader.readAsText(uploadFile)
    reader.onload = function (f) {
        if (this.result && typeof this.result === 'string') {
            const datas = JSON.parse(this.result)
            db.setDB(datas)
        }
    }

    return false
}

onMounted(() => {
    const btnConfigJson = localStorage.getItem(btnDbKey)
    const btnConfig = btnConfigJson ? JSON.parse(btnConfigJson) : []
    if ((btnConfig?.includes('openMockRecord') || config.openMockRecord)) {
        handleChangeMockRecord()
    }
    if ((btnConfig?.includes('openMockMode') || config.openMockMode)) {
        handleChangeMockMode()
    }
})

// 脱敏操作
interface D13NDATA {
    id: string,
    selectInterface: string,
    excludeInterface: string,
    field: string,
    type: "1" | "2" | "3" | "4" | "5",
    rangeMin: string,
    rangeMax: string,
}
const rulesKey = "vue__mock_rules";
const showD13n = ref(false);
const d13nData = ref<D13NDATA[]>([]);
onMounted(() => {
    const localData = window.localStorage.getItem(rulesKey);
    d13nData.value = localData ? JSON.parse(localData) : [];
})
watch(d13nData, () => {
    window.localStorage.setItem(rulesKey, JSON.stringify(d13nData.value));
}, { deep: true })
const minPh = (scope) => {
    const ph = [
        "",
        "前部分保留字符数",
        "",
        "最小数字",
        "待替换字符串",
        "正则表达式",
    ];
    return ph[Number(scope.row.type)];
}
const maxPh = (scope) => {
    const ph = [
        "",
        "后部分保留字符数",
        "",
        "最大数字",
        "替换为什么字符串",
        "替换为什么字符串",
    ];
    return ph[Number(scope.row.type)];
}
const addRule = () => {
    d13nData.value.push({
        id: nanoid(),
        selectInterface: "",
        excludeInterface: "",
        field: "",
        type: "1",
        rangeMin: "",
        rangeMax: "",
    });
}
const removeRule = (scope) => {
    const index = d13nData.value.findIndex(i => i.id === scope.row.id);
    if (index !== -1) {
        d13nData.value.splice(index, 1);
    }
}
</script>

<style scoped>
.vmock {
    position: fixed;
    bottom: 100px;
    right: 20px;
    z-index: 999999;
}

.upload :deep(.el-upload), .upload :deep(.el-dropdown-menu__item) {
    width: 100%;
}

.rule {
    display: flex;
}
</style>